/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package shaman.storage.impl.filter.crypto;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;

/**
 *
 * @author dmitriy
 */
public class PasswordKeySpec extends SecretKeySpec {

    private static final String PASSWORD_ENCODING="UTF-8";
    private static final String DIGEST_ALGORITHM="SHA-1";
    private static final int DIGEST_ALGORITHM_BLOCKLEN=512;

    public PasswordKeySpec(String password, byte[] salt, int size, int iterCount, String algorithm) throws IOException {
        super(hashPassword(password, salt, size, iterCount), algorithm);
    }

    /**
     * RFC2104-compliant HMAC implementation
     * @param password
     * The password to be hashed
     * @param salt
     * The salt bytes, if used
     * @param size
     * The key size required
     * @param iterCount
     * The iterations count to be used
     * @return
     * The raw key data produced
     * @throws IOException
     * If UTF-8 encoding or SHA1 digest algorithm is not supported
     */
    public static byte[] hashPassword(String password, byte[] salt, int size, int iterCount) throws IOException {
        byte[] passwordBytes;
        try {
            passwordBytes=password.getBytes(PASSWORD_ENCODING);
        } catch (UnsupportedEncodingException ex) {
            throw new IOException("Кодировка "+PASSWORD_ENCODING+" не поддерживается");
        }
        MessageDigest digest;
        try {
            digest=MessageDigest.getInstance(DIGEST_ALGORITHM);
        } catch (NoSuchAlgorithmException ex) {
            throw new IOException("Хэш-функция "+DIGEST_ALGORITHM+" не поддерживается");
        }
        byte[] keyBytes=new byte[size];
        HmacCalculator hmac=new HmacCalculator(passwordBytes, digest, DIGEST_ALGORITHM_BLOCKLEN);
        byte[] ui, ti;
        int l=hmac.engineGetDigestLength();
        int blockIndex, startByte;
        hmac.engineReset();
        for (blockIndex=1, startByte=0; startByte<keyBytes.length; blockIndex++, startByte+=l)
        {
            int i;
            hmac.engineUpdate(salt, 0, salt.length);
            hmac.engineUpdate(NumberUtil.intBytesBe(blockIndex), 0, 4);
            ui=hmac.engineDigest();
            ti=ui.clone();
            for (i=1; i<iterCount; i++)
            {
                hmac.engineUpdate(ui, 0, ui.length);
                ui=hmac.engineDigest();
                NumberUtil.bitwiseXor(ti, ui);
            }
            System.arraycopy(ti, 0, keyBytes, startByte, Math.min(ti.length, keyBytes.length-startByte));
        }
        return keyBytes;
    }

}
